package com.elong.common.http;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import com.elong.common.constant.CheckCodeConst;

/**
 * @ClassName: FluentAPI
 * 
 * @Description: http访问的便利api
 * 
 * @author Guanguo.Gao
 * 
 * @date 2014年9月11日 下午4:04:38
 * 
 * @version V1.0
 */
public class FluentHttpsAPI {

    // 日志
    private static Log log = LogFactory.getLog(FluentHttpsAPI.class);

    // 统一的编码UTF-8
    public static final String DEFAULT_URL_ENCODE = "UTF-8";

    public static final String URL_PARAM_CONNECT = "&";

    // 连接管理器
    private static PoolingHttpClientConnectionManager manager = null;

    // 连接超时
    // private static int connectionTimeOut = 25000;

    // 默认buff size
    private static int maxBuffSize = 8192;

    // socket超时
    private static int socketTimeOut = 25000;

    // 每个host最多的连接数
    private static int maxConnectionPerHost = 20;

    // 最多的连接数
    private static int maxTotalConnections = 100;

    private static HttpClient client;

    // client context
    private HttpClientContext context = HttpClientContext.create();

    // cookie store
    private CookieStore cookieStore = new BasicCookieStore();

    static {
        manager = new PoolingHttpClientConnectionManager();
        manager.setDefaultMaxPerRoute(maxConnectionPerHost);
        manager.setMaxTotal(maxTotalConnections);
        // set socket option
        SocketConfig socketConfig = SocketConfig.custom()
                .setSoTimeout(socketTimeOut).build();
        manager.setDefaultSocketConfig(socketConfig);
        // set connection config
        ConnectionConfig connConfig = ConnectionConfig.custom()
                .setCharset(Consts.UTF_8).setBufferSize(maxBuffSize).build();
        manager.setDefaultConnectionConfig(connConfig);
    }

    public FluentHttpsAPI() {
        client = createSSLClientDefault();
    }

    /**
     * 创建CloseableHttpClient
     */
    public static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(
                    null, new TrustStrategy() {

                        public boolean isTrusted(X509Certificate[] chain,
                                String authType) throws CertificateException {
                            return true;
                        }
                    }).build();

            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslContext);

            CredentialsProvider credProvider = new BasicCredentialsProvider();
            UsernamePasswordCredentials cred = new UsernamePasswordCredentials(
                    "test", "test");
            credProvider.setCredentials(new AuthScope(AuthScope.ANY_HOST,
                    AuthScope.ANY_PORT), cred);
            CloseableHttpClient clients = HttpClients.custom()
                    .setSSLSocketFactory(sslsf)
                    .setHostnameVerifier(new CustomHostNameVerifier())
                    .setRedirectStrategy(new CustomRedirectStrategy())
                    // .setProxy(proxy)
                    // .setDefaultCredentialsProvider(credProvider)
                    // .setConnectionManager(manager)
                    .build();
            return clients;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }

    // verifier class
    private static class CustomHostNameVerifier implements X509HostnameVerifier {

        public boolean verify(String hostname, SSLSession session) {
            return true;
        }

        public void verify(String host, SSLSocket ssl) throws IOException {
        }

        public void verify(String host, X509Certificate cert)
                throws SSLException {
        }

        public void verify(String host, String[] cns, String[] subjectAlts)
                throws SSLException {
        }

    }
    
    /**
     * @Description: urlget数据
     * @param url
     * @param isReserveCookie
     * @return
     * @throws
     */
    public HttpResult URLGet(String url) {
        return URLGet(url, null);
    }

    /**
     * @Description: urlget数据
     * @param url
     * @param params
     * @param isReserveCookie
     * @return
     * @throws
     */
    public HttpResult URLGet(String url, Map<String, String> params) {
        return URLGet(url, params, false);
    }

    /**
     * @Description: urlget数据
     * @param url
     * @param params
     * @param isReserveCookie
     * @return
     * @throws
     */
    public HttpResult URLGet(String url, Map<String, String> params,
            boolean isReserveCookie) {
        return URLGet(url, params, DEFAULT_URL_ENCODE, isReserveCookie);
    }

    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public HttpResult URLGet(String url, Map<String, String> params,
            String enc, boolean isReserveCookie) {
        StringBuffer strtTotalURL = new StringBuffer("");
        strtTotalURL.append(url);
        if (params != null) {
            if (strtTotalURL.indexOf("?") == -1) {
                strtTotalURL.append("?").append(getUrl(params, enc));
            } else {
                strtTotalURL.append("&").append(getUrl(params, enc));
            }
        }
        log.debug("GET请求URL = \n" + strtTotalURL.toString());

        HttpGet httpget = new HttpGet(strtTotalURL.toString());
        setHttpHeader(httpget);
        HttpResponse response = null;
        HttpResult result = null;
        try {
            response = client.execute(httpget, context);
            result = HttpUtil.getContentFromResponse(response);
            Integer retCode = result.getRetCode();
            if (retCode == HttpStatus.SC_MOVED_TEMPORARILY
                    || retCode == HttpStatus.SC_MOVED_PERMANENTLY
                    || retCode == HttpStatus.SC_SEE_OTHER
                    || retCode == HttpStatus.SC_TEMPORARY_REDIRECT) {
                String redirectUrl = HttpUtil.getRedirectUrl(httpget, response,
                        context);
                httpget = new HttpGet(redirectUrl);
                response = client.execute(httpget, context);
                result = HttpUtil.getContentFromResponse(response);
            }
            if (isReserveCookie)
                result.setCookies(cookieStore.getCookies());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response = null;
            httpget.releaseConnection();
        }
        return result;
    }

    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public HttpResult URLGetImage(String url) {
        HttpGet httpget = new HttpGet(url.toString());
        setHttpHeader(httpget);
        HttpResponse response = null;
        HttpResult result = null;
        try {
            response = client.execute(httpget, context);
            writeImageToFile(response);
            result = HttpUtil.getContentFromResponse(response);
            Integer retCode = result.getRetCode();
            if (retCode == HttpStatus.SC_MOVED_TEMPORARILY
                    || retCode == HttpStatus.SC_MOVED_PERMANENTLY
                    || retCode == HttpStatus.SC_SEE_OTHER
                    || retCode == HttpStatus.SC_TEMPORARY_REDIRECT) {
                String redirectUrl = HttpUtil.getRedirectUrl(httpget, response,
                        context);
                httpget = new HttpGet(redirectUrl);
                response = client.execute(httpget, context);
                result = (HttpImageResult) HttpUtil
                        .getContentFromResponse(response);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response = null;
            httpget.releaseConnection();
        }
        return result;
    }

    /**
     * @Description: 结果写入文件
     * @param response
     * @throws
     */
    private static void writeImageToFile(HttpResponse response) {
        HttpEntity entity = response.getEntity();
        File imageFile = new File("images/"+CheckCodeConst.train_12306+".jpg");
        try {
            FileOutputStream fos = new FileOutputStream(imageFile);
            if (entity != null) {
                InputStream instream = entity.getContent();
                int len;
                BufferedInputStream in = new BufferedInputStream(instream);
                byte[] buf = new byte[1024];
                while ((len = in.read(buf, 0, 1024) ) != -1) {
                    fos.write(buf, 0, len);
                }
                fos.flush();
                instream.close();
                fos.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
                
        }
    }

    /**
     * @Description: url post 请求
     * @param url
     * @param params
     * @return
     * @throws
     */
    public HttpResult URLPost(String url, Map<String, String> params) {
        return URLPost(url, params, false);
    }

    public HttpResult URLPost(String url, Map<String, String> params,
            boolean isReserveCookie) {
        return URLPost(url, params, DEFAULT_URL_ENCODE, isReserveCookie);
    }

    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public HttpResult URLPost(String url, Map<String, String> params,
            String enc, boolean isReserveCookie) {
        HttpResult result = null;
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        for (String key : params.keySet()) {
            formparams.add(new BasicNameValuePair(key, params.get(key)));
        }
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
                Consts.UTF_8);
        HttpPost httppost = new HttpPost(url);
        httppost.setEntity(entity);
        setHttpHeader(httppost);
        HttpResponse response = null;
        try {
            if (isReserveCookie)
                response = client.execute(httppost, context);
            else {
                response = client.execute(httppost);
            }
            result = HttpUtil.getContentFromResponse(response);
            if (isReserveCookie)
                result.setCookies(cookieStore.getCookies());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response = null;
            httppost.releaseConnection();
        }
        return result;
    }

    /**
     * 据Map生成URL字符串
     * 
     * @param map
     *            Map
     * @param valueEnc
     *            URL编码
     * @return URL
     */
    private static String getUrl(Map<String, String> map, String valueEnc) {

        if (null == map || map.keySet().size() == 0) {
            return "";
        }
        StringBuffer url = new StringBuffer();
        Set<String> keys = map.keySet();
        for (Iterator<String> it = keys.iterator(); it.hasNext();) {
            String key = it.next();
            if (map.containsKey(key)) {
                String val = map.get(key);
                String str = val != null ? val : "";
                try {
                    str = URLEncoder.encode(str, valueEnc);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                url.append(key).append("=").append(str)
                        .append(URL_PARAM_CONNECT);
            }
        }
        String strURL = "";
        strURL = url.toString();
        if (URL_PARAM_CONNECT.equals("" + strURL.charAt(strURL.length() - 1))) {
            strURL = strURL.substring(0, strURL.length() - 1);
        }

        return (strURL);
    }

    /** 设置文件头 **/
    public static void setHttpHeader(HttpRequestBase base) {
        base.addHeader(
                "User-Agent",
                "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36");
        base.addHeader("Accept",
                "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        base.addHeader("Accept-Language", "en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4");
        base.addHeader("Connection", "keep-alive");
    }

    public static void main(String[] args) {
        HttpResult result = new FluentHttpsAPI().URLGet("http://www.elong.com",
                null);
        System.out.println(result.getRetContent());
    }
}
